using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MobileTDL
{
	public partial class MainForm : Form
	{
		private bool _changed;
		private TreeNode _clipboardNode;
		private ToDoListFile _currentFile;
		private string _currentFilename = "";
		private bool _cut;
		private Settings _settings;

		public MainForm()
		{
			InitializeComponent();
		}

		private bool Changed
		{
			get { return _changed; }
			set { _changed = value; }
		}

		private TreeNode ClipboardNode
		{
			get { return _clipboardNode; }
			set { _clipboardNode = value; }
		}

		public ToDoListFile CurrentFile
		{
			get { return _currentFile; }
			set
			{
				_currentFile = value;
                Changed = false;

                // Handle the null value on closing the file. No need to refresh the nodes.
                if (value == null)
                    return;

                RefreshData();
			}
		}

		public string CurrentFilename
		{
			get { return _currentFilename; }
			set { _currentFilename = value; }
		}

		private bool Cut
		{
			get { return _cut; }
			set { _cut = value; }
		}

		public Settings Settings
		{
			get { return _settings; }
			set
			{
				_settings = value;
				UseSettings();
			}
		}

		private bool CloseFile()
		{
			bool cancelled = false;
			if (Changed)
			{
				DialogResult result = MessageBox.Show("Save changes?", Text, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
				switch (result)
				{
					case DialogResult.Cancel: cancelled = true; break;
					case DialogResult.No: CurrentFile = null; break;
					case DialogResult.Yes: SaveFile(); CurrentFile = null; break;
				}
			}
			else 
                CurrentFile = null;

			if (!cancelled) Close();
			return cancelled;
		}

        /// <summary>
        /// Fill the tasks, starting from given node. 
        /// </summary>
        /// <param name="node">Node to fill or refresh. If null, start from the root node.</param>
        /// <param name="list"></param>
        /// <returns></returns>
		private TreeNode FillNode(TreeNode node, ToDoList list)
		{
			if (node == null) node = new TreeNode();
			node.Checked = false;
			node.Tag = list;
			node.Text = list.ProjectName;
			if (list.Items.Count > 0 && list.Items.Count != node.Nodes.Count)
			{
				node.Nodes.Add(new TreeNode("Loading..."));
			}
			return node;
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <param name="task"></param>
        /// <returns></returns>
		private TreeNode FillNode(TreeNode node, ToDoTask task)
		{
			if (node == null) node = new TreeNode();
			//node.BackColor = task.PriorityColor;
			node.Checked = task.Completed;
			node.ForeColor = task.PriorityColor;
			node.Tag = task;

			string extraText = "";
			string fieldText;
			foreach (DisplayFieldType fieldType in Settings.DisplayFields)
			{
				fieldText = "";
				switch (fieldType)
				{
					case DisplayFieldType.Category: fieldText += task.Category; break;
					case DisplayFieldType.DueDate: fieldText += (task.DueDate.HasValue ? task.DueDate.Value.ToString("yyyy-MM-dd") : ""); break;
					case DisplayFieldType.PercentageDone: fieldText += (task.PercentDone.HasValue ? task.PercentDone.ToString() : ""); break;
					case DisplayFieldType.Person: fieldText += task.Person; break;
					case DisplayFieldType.Priority: fieldText += (task.Priority.HasValue ? task.Priority.ToString() : ""); break;
					case DisplayFieldType.Risk: fieldText += task.Risk; break;
					case DisplayFieldType.Status: fieldText += task.Status; break;
				}
				if (fieldText.Length > 0)
				{
					if (extraText.Length > 0) extraText += ",";
					extraText += fieldText;
				}
			}
			node.Text = task.Title + (extraText.Length > 0 ? node.Text += " {" + extraText + "}" : "");

			if (task.Items.Count > 0 && task.Items.Count != node.Nodes.Count) node.Nodes.Add(new TreeNode("Loading..."));
			return node;
		}

		private void HandleException(Exception ex)
		{
			MessageBox.Show(ex.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
		}

		private void RefreshData()
		{
			bool enabled = (_currentFile != null);
			txtDetails.Text = "";
			tvTasks.BeginUpdate();
			try
			{
				tvTasks.Nodes.Clear();
				if (enabled)
				{
					TreeNode node = FillNode(null, _currentFile.ToDoList);
					tvTasks.Nodes.Add(node);
					node.Expand();
				}
			}
			finally
			{
				tvTasks.EndUpdate();
			}
			tvTasks.Enabled = enabled;
			btnAdd.Enabled = enabled;
			txtNewTaskTitle.Enabled = enabled;
		}

		private void SaveFile()
		{
			if (CurrentFile == null) throw new NullReferenceException("There is no file to save");
			if (CurrentFilename.Length == 0) SaveFileAs();
			else
			{
				CurrentFile.Save(CurrentFilename);
				Changed = false;
			}
		}

		private void SaveFileAs()
		{
			if (CurrentFile == null) throw new NullReferenceException("There is no file to save");
			if (saveFileDialog1.ShowDialog() == DialogResult.OK)
			{
				CurrentFilename = saveFileDialog1.FileName;
				CurrentFile.Save(CurrentFilename);
				Changed = false;
			}
		}

		private void UseSettings()
		{
			txtComments.Visible = Settings.ShowComments;
			txtDetails.Visible = Settings.ShowDetails;
		}

        /// <summary>
        /// Add a new node.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
		private void btnAdd_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null && tvTasks.SelectedNode.Tag is ToDoTaskContainer)
				{
					ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Tag;
					ToDoTask task = parent.CreateTask();
					task.Title = txtNewTaskTitle.Text;
					parent.Items.Add(task);
					tvTasks.SelectedNode.Nodes.Add(FillNode(null, task));
					Changed = true;
					txtNewTaskTitle.SelectAll();
					tvTasks.SelectedNode.Expand();
					Changed = true;

                    // clear entry box after adding the node.
                    txtNewTaskTitle.Text = string.Empty;
				}
				else MessageBox.Show("Cannot add child to this item.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void cmiTaskEdit_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null)
				{
					if (tvTasks.SelectedNode.Tag is ToDoList)
					{
						ListForm frm = new ListForm();
						ToDoList list = (ToDoList)tvTasks.SelectedNode.Tag;
						frm.ToDoList = list;
						if (frm.ShowDialog() == DialogResult.OK)
						{
							FillNode(tvTasks.SelectedNode, list);
							txtDetails.Text = list.ToString();
							Changed = true;
						}
					}
					else if (tvTasks.SelectedNode.Tag is ToDoTask)
					{
						TaskForm frm = new TaskForm();
						ToDoTask task = (ToDoTask)tvTasks.SelectedNode.Tag;
						frm.Task = task;
						if (frm.ShowDialog() == DialogResult.OK)
						{
							FillNode(tvTasks.SelectedNode, task);
							txtComments.Text = task.Comments;
							txtDetails.Text = task.ToString();
							Changed = true;
						}
					}
				}
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void cmiTaskNewChild_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null && tvTasks.SelectedNode.Tag is ToDoTaskContainer)
				{
					ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Tag;
					ToDoTask task = parent.CreateTask();
					TaskForm frm = new TaskForm();
					frm.Task = task;
					if (frm.ShowDialog() == DialogResult.OK)
					{
						parent.Items.Add(task);
						tvTasks.SelectedNode.Nodes.Add(FillNode(null, task));
						tvTasks.SelectedNode.Expand();
						Changed = true;
					}
					//else parent.Items.Remove(task);
				}
				else MessageBox.Show("Cannot add child to this item.", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void cmiTaskNewSibling_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null && tvTasks.SelectedNode.Parent != null
					&& tvTasks.SelectedNode.Parent.Tag is ToDoTaskContainer)
				{
					ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Parent.Tag;
					TaskForm frm = new TaskForm();
					ToDoTask task = parent.CreateTask();
					frm.Task = task;
					if (frm.ShowDialog() == DialogResult.OK)
					{
						parent.Items.Add(task);
						tvTasks.SelectedNode.Parent.Nodes.Add(FillNode(null, task));
						Changed = true;
					}
					//else parent.Items.Remove(task);
				}
				else MessageBox.Show("Cannot add sibling to this item", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miFileClose_Click(object sender, EventArgs e)
		{
			try
			{
				CloseFile();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miFileSave_Click(object sender, EventArgs e)
		{
			try
			{
				SaveFile();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miFileSaveAs_Click(object sender, EventArgs e)
		{
			try
			{
				SaveFileAs();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miFileSettings_Click(object sender, EventArgs e)
		{
			try
			{
				SettingsForm frm = new SettingsForm();
				frm.Settings = Settings;
				if (frm.ShowDialog() == DialogResult.OK) UseSettings();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuShowUncompleted_Click(object sender, EventArgs e)
		{
			try
			{
				miMenuShowUncompleted.Checked = !miMenuShowUncompleted.Checked;
				RefreshData();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuShowCompleted_Click(object sender, EventArgs e)
		{
			try
			{
				miMenuShowCompleted.Checked = !miMenuShowCompleted.Checked;
				RefreshData();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void tvTasks_AfterCheck(object sender, TreeViewEventArgs e)
		{
            // Don't allow changing completion here, for now.
            // e.Node.Checked = false;
            return;

			try
			{
				if (e.Node != null)
				{
					if (e.Node.Tag is ToDoList)
					{
						if (e.Node.Checked) e.Node.Checked = false;
					}
					else if (e.Node.Tag is ToDoTask)
					{
						ToDoTask task = (ToDoTask)e.Node.Tag;
						if (e.Node.Checked) task.SetComplete();
						else task.SetUncomplete(Settings.UncompletePercentage);
						Changed = true;

                        // Refresh node.
                        // Can't do it here because this event is called on initial load.
                        // FillNode(e.Node, task);
					}
				}
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void tvTasks_AfterExpand(object sender, TreeViewEventArgs e)
		{
			try
			{
				if (e.Node.Tag is ToDoTaskContainer)
				{
					tvTasks.BeginUpdate();
					try
					{
						((ToDoTaskContainer)e.Node.Tag).Sort(Settings.SortType);
						e.Node.Nodes.Clear();
						foreach (ToDoTask task in ((ToDoTaskContainer)e.Node.Tag).Items)
						{
							if ((miMenuShowCompleted.Checked && task.ContainsCompleted)
								|| (miMenuShowUncompleted.Checked && task.ContainsIncompleted)) e.Node.Nodes.Add(FillNode(null, task));
						}
					}
					finally
					{
						tvTasks.EndUpdate();
					}
				}
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

        /// <summary>
        /// After an item is selected. Display details and comments.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
		private void tvTasks_AfterSelect(object sender, TreeViewEventArgs e)
		{
			try
			{
				if (e.Node.Tag != null) txtDetails.Text = e.Node.Tag.ToString();
				if (e.Node.Tag is ToDoTask) txtComments.Text = ((ToDoTask)e.Node.Tag).Comments;
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void txtNewTaskTitle_GotFocus(object sender, EventArgs e)
		{
			try
			{
				inputPanel1.Enabled = true;
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void txtNewTaskTitle_LostFocus(object sender, EventArgs e)
		{
			try
			{
				inputPanel1.Enabled = false;
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void MainForm_Closing(object sender, CancelEventArgs e)
		{
			try
			{
				e.Cancel = CloseFile();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			//try
			//{
			//}
			//catch (Exception ex)
			//{
			//  HandleException(ex);
			//}
        }

        #region menu handlers

        private void miMenuCut_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null)
				{
					ClipboardNode = tvTasks.SelectedNode;
					Cut = true;
				}
				else ClipboardNode = null;
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuCopy_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null)
				{
					ClipboardNode = tvTasks.SelectedNode;
					Cut = false;
				}
				else ClipboardNode = null;
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuPaste_Click(object sender, EventArgs e)
		{
			try
			{
				if (ClipboardNode != null && ClipboardNode.Tag is ToDoTask && tvTasks.SelectedNode != null)
				{
					if (tvTasks.SelectedNode != null && tvTasks.SelectedNode.Tag is ToDoTaskContainer)
					{
						TreeNode newParentNode = tvTasks.SelectedNode;
						ToDoTaskContainer newTaskContainer = (ToDoTaskContainer)tvTasks.SelectedNode.Tag;
						ToDoTask clipboardTask = (ToDoTask)ClipboardNode.Tag;
						if (Cut)
						{
							if (ClipboardNode.Parent != null && ClipboardNode.Parent.Tag is ToDoTaskContainer)
							{
								ToDoTaskContainer oldTaskContainer = (ToDoTaskContainer)ClipboardNode.Parent.Tag;

								oldTaskContainer.Items.Remove(clipboardTask);
								ClipboardNode.Remove();

								newTaskContainer.Items.Add(clipboardTask);
								newParentNode.Nodes.Add(ClipboardNode);

								ClipboardNode = null;
							}
						}
						else
						{
							ToDoTask copiedTask = (ToDoTask)clipboardTask.Clone();
							newTaskContainer.Items.Add(copiedTask);
							tvTasks.SelectedNode.Nodes.Add(FillNode(null, copiedTask));
						}
						Changed = true;
					}
				}
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuDelete_Click(object sender, EventArgs e)
		{
			try
			{
				if (tvTasks.SelectedNode != null && tvTasks.SelectedNode.Tag is ToDoTask
					&& tvTasks.SelectedNode.Parent != null && tvTasks.SelectedNode.Parent.Tag is ToDoTaskContainer)
				{
					ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Parent.Tag;
					ToDoTask task = (ToDoTask)tvTasks.SelectedNode.Tag;
					if (MessageBox.Show("Delete task: " + task.Title, Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
					{
						parent.Items.Remove(task);
						tvTasks.SelectedNode.Remove();
						Changed = true;
					}
				}
				else MessageBox.Show("Cannot delete this item", Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuExpandAll_Click(object sender, EventArgs e)
		{
			try
			{
				tvTasks.ExpandAll();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

		private void miMenuCollapseAll_Click(object sender, EventArgs e)
		{
			try
			{
				tvTasks.CollapseAll();
			}
			catch (Exception ex)
			{
				HandleException(ex);
			}
		}

#endregion

        private void MoveUpMenuItem_Click(object sender, EventArgs e)
        {
            // Move node 1 level up, if possible.

            // Get currently selected node.
            ToDoTask task;

            if (!validateSelectedNode())
                return;
            else
                task = this.SelectedTask;

            // Check if it can be moved up.
            if (task.Position.HasValue && task.Position > 1)
            {
                // We can do this. Reposition the above node and this node.
                // Get the current selected node position. Change the current node's position to n-1.
                int taskIndex = tvTasks.SelectedNode.Index;
                int originalPosition = task.Position ?? 0;

                TreeNode parentNode = tvTasks.SelectedNode.Parent;
                ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Parent.Tag;
                ToDoTask taskAbove = parent.Items.Find(s => s.Position == task.Position - 1);

                // Note: Position is NOT incremental by 1. There are cases when a task is deleted, 
                // position is not recalculated. Need a fix to literally swap the position values,
                // instead of simply incrementing or decrementing them.

                this.swapPositions(task, taskAbove);

                Changed = true;

                // Refresh parent node.
                refreshNode(parentNode);

                // Select again the current node, because selection has now shifted to the parent.
                tvTasks.SelectedNode = parentNode.Nodes[taskIndex - 1];
            }

        }

        /// <summary>
        /// Check if a valid node is selected for operations.
        /// </summary>
        /// <returns></returns>
        private bool validateSelectedNode()
        {
            if (tvTasks.SelectedNode == null)
            {
                MessageBox.Show("Please select a task.");
                return false;
            }
            if (!(tvTasks.SelectedNode.Tag is ToDoTask))
            {
                MessageBox.Show("Please select a valid task.");
                return false;
            }

            return true;
        }

        private ToDoTask SelectedTask
        {
            get
            {
                // do validation here?

                ToDoTask task = (ToDoTask)tvTasks.SelectedNode.Tag;
                return task;
            }
        }

        /// <summary>
        /// Update the treeview node with the value(s) from the underlying model.
        /// </summary>
        private void refreshNode(TreeNode node)
        {
            tvTasks.BeginUpdate();

            // Collapse the node to enable auto-refresh of child nodes when expanded.
            node.Collapse();

            // Update this node values.
            if (node.Tag is ToDoList)
            {
                ToDoList tdl = (ToDoList)node.Tag;
                FillNode(node, tdl);
            }
            if (node.Tag is ToDoTask)
            {
                ToDoTask task = (ToDoTask)node.Tag;
                FillNode(node, task);
            }

            // Update all child nodes.
            node.Expand();
            // Remove child nodes.
            // node.Nodes.Clear();

            tvTasks.EndUpdate();
        }

        private void MoveDownMenuItem_Click(object sender, EventArgs e)
        {
            // Move node 1 level down, if possible.

            // Get currently selected node.
            ToDoTask task;

            if (!validateSelectedNode())
                return;
            else
                task = this.SelectedTask;

            // Check if it can be moved down.
            ToDoTaskContainer parent = (ToDoTaskContainer)tvTasks.SelectedNode.Parent.Tag;
            if (task.Position.HasValue && task.Position < parent.Items.Count)
            {
                // We can do this. Swap the node below with this node.
                // Get the current selected node position. Change the current node's position to n+1.
                int taskIndex = tvTasks.SelectedNode.Index;
                TreeNode parentNode = tvTasks.SelectedNode.Parent;
                ToDoTask taskBelow = parent.Items.Find(s => s.Position == task.Position + 1);

                swapPositions(task, taskBelow);

                Changed = true;

                // Refresh parent node.
                refreshNode(parentNode);

                // Select again the current node, because selection has now shifted to the parent.
                tvTasks.SelectedNode = parentNode.Nodes[taskIndex + 1];
            }
        }

        private void swapPositions(ToDoTask task1, ToDoTask task2)
        {
            // Fix the position values if they are equal.
            if (task1.Position == task2.Position)
            {
                task2.Position += 1;
            }

            int originalPosition = task1.Position ?? 0;

            task1.Position = task2.Position;
            task2.Position = originalPosition;

        }
	}
}